package com.atguigu.gmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.pms.entity.BrandEntity;
import com.atguigu.gmall.pms.entity.CategoryEntity;
import com.atguigu.gmall.search.pojo.Goods;
import com.atguigu.gmall.search.pojo.SearchParamVo;
import com.atguigu.gmall.search.pojo.SearchResponseAttrVo;
import com.atguigu.gmall.search.pojo.SearchResponseVo;
import com.atguigu.gmall.search.service.SearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @description:
 * @author: xie.zh
 * @create: 2021.03.16   20:27
 * @version: 1.0
 */
@Service
public class SearchServiceImpl implements SearchService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public SearchResponseVo search(SearchParamVo searchParamVo) {
        try {
            SearchResponse searchResponse = restHighLevelClient.search(new SearchRequest(new String[]{"goods"},
                    this.buildDsl(searchParamVo)), RequestOptions.DEFAULT);
            SearchResponseVo searchResponseVo = parseResult(searchResponse);
            searchResponseVo.setPageNum(searchParamVo.getPageNum());
            searchResponseVo.setPageSize(searchParamVo.getPageSize());
            return searchResponseVo;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private SearchResponseVo parseResult(SearchResponse searchResponse) {
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        //解析hits :总记录数 和 goods列表
        SearchHits searchHits = searchResponse.getHits();
        //总记录数
        searchResponseVo.setTotal(searchHits.getTotalHits());
        SearchHit[] hits = searchHits.getHits();
        //goods列表
        searchResponseVo.setGoodsList(Stream.of(hits).map(hit -> {
            //获取hits对象中的_source  -- json字符串
            String source = hit.getSourceAsString();
            //反序列化
            Goods goods = JSON.parseObject(source, Goods.class);
            //获取高亮列表
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                //获取高亮title字段
                HighlightField title = highlightFields.get("title");
                if (title != null) {
                    Text titleFragment = title.getFragments()[0];
                    if (titleFragment != null) {
                        //设置title为高亮title字段内容
                        goods.setTitle(titleFragment.toString());
                    }
                }
            }
            return goods;
        }).collect(Collectors.toList()));

        //解析aggregation : 品牌 分类 规格参数
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();

        //获取brandIdAgg 解析品牌聚合结果集
        ParsedLongTerms brandIdAgg = (ParsedLongTerms) aggregationMap.get("brandIdAgg");
        if (brandIdAgg != null) {
            //获取buckets
            List<? extends Terms.Bucket> brandIdAggBuckets = brandIdAgg.getBuckets();
            //设置品牌列表
            if (!CollectionUtils.isEmpty(brandIdAggBuckets)) {
                searchResponseVo.setBrands(brandIdAggBuckets.stream().map(brandIdBucket -> {
                    BrandEntity brandEntity = new BrandEntity();
                    Map<String, Aggregation> brandIdMap = brandIdBucket.getAggregations().asMap();
                    //获取桶中的key 设置为品牌id
                    brandEntity.setId(brandIdBucket.getKeyAsNumber().longValue());
                    //获取品牌名子聚合
                    ParsedStringTerms brandNameAgg = (ParsedStringTerms) brandIdMap.get("brandNameAgg");
                    if (brandNameAgg != null) {
                        //设置品牌名
                        List<? extends Terms.Bucket> brandNameAggBuckets = brandNameAgg.getBuckets();
                        if (!CollectionUtils.isEmpty(brandNameAggBuckets)) {
                            Terms.Bucket brandBucket = brandNameAggBuckets.get(0);
                            if (brandBucket != null) {
                                brandEntity.setName(brandBucket.getKeyAsString());
                            }
                        }
                    }
                    //获取logo子聚合
                    ParsedStringTerms logoAgg = (ParsedStringTerms) brandIdMap.get("logoAgg");
                    if (logoAgg != null) {
                        List<? extends Terms.Bucket> logoAggBuckets = logoAgg.getBuckets();
                        if (!CollectionUtils.isEmpty(logoAggBuckets)) {
                            Terms.Bucket logoBucket = logoAggBuckets.get(0);
                            if (logoBucket != null) {
                                brandEntity.setLogo(logoBucket.getKeyAsString());
                            }
                        }
                    }
                    return brandEntity;
                }).collect(Collectors.toList()));
            }
        }

        //解析分类的聚合结果集 获取分类的过滤条件
        ParsedLongTerms categoryIdAgg = (ParsedLongTerms) aggregationMap.get("categoryIdAgg");
        if (categoryIdAgg != null) {
            //获取分类聚合桶
            List<? extends Terms.Bucket> categoryIdAggBuckets = categoryIdAgg.getBuckets();
            if (!CollectionUtils.isEmpty(categoryIdAggBuckets)) {
                //设置分类对象
                searchResponseVo.setCategories(categoryIdAggBuckets.stream().map(categoryIdBuckets -> {
                    CategoryEntity categoryEntity = new CategoryEntity();
                    //设置categoryId
                    categoryEntity.setId(categoryIdBuckets.getKeyAsNumber().longValue());
                    //获取子聚合
                    Map<String, Aggregation> categoryIdMap = categoryIdBuckets.getAggregations().asMap();
                    //categoryNameAgg
                    ParsedStringTerms categoryNameAgg = (ParsedStringTerms) categoryIdMap.get("categoryNameAgg");
                    if (categoryNameAgg != null) {
                        List<? extends Terms.Bucket> categoryNameAggBuckets = categoryNameAgg.getBuckets();
                        if (!CollectionUtils.isEmpty(categoryNameAggBuckets)) {
                            Terms.Bucket categoryNameBucket = categoryNameAggBuckets.get(0);
                            if (categoryNameBucket != null) {
                                //设置categoryName
                                categoryEntity.setName(categoryNameBucket.getKeyAsString());
                            }
                        }
                    }
                    return categoryEntity;
                }).collect(Collectors.toList()));
            }
        }

        //解析规格参数的聚合结果集 获取属性过滤条件
        ParsedNested attrAgg = (ParsedNested) aggregationMap.get("attrAgg");
        if (attrAgg != null) {
            ParsedLongTerms attrIdAgg = attrAgg.getAggregations().get("attrIdAgg");
            if (attrIdAgg != null) {
                List<? extends Terms.Bucket> attrIdAggBuckets = attrIdAgg.getBuckets();
                if (!CollectionUtils.isEmpty(attrIdAggBuckets)) {
                    searchResponseVo.setFilters(attrIdAggBuckets.stream().map(attrIdBucket -> {
                        SearchResponseAttrVo searchResponseAttrVo = new SearchResponseAttrVo();
                        //设置AttrId
                        searchResponseAttrVo.setAttrId(attrIdBucket.getKeyAsNumber().longValue());
                        //获取子聚合  attrNameAgg
                        ParsedStringTerms attrNameAgg = attrIdBucket.getAggregations().get("attrNameAgg");
                        if (attrNameAgg != null) {
                            List<? extends Terms.Bucket> attrNameAggBuckets = attrNameAgg.getBuckets();
                            if (!CollectionUtils.isEmpty(attrNameAggBuckets)) {
                                Terms.Bucket attrNameBucket = attrNameAggBuckets.get(0);
                                if (attrNameBucket != null) {
                                    //设置AttrName
                                    searchResponseAttrVo.setAttrName(attrNameBucket.getKeyAsString());
                                }
                            }
                        }
                        //获取子聚合  attrValueAgg
                        ParsedStringTerms attrValueAgg = attrIdBucket.getAggregations().get("attrValueAgg");
                        if (attrValueAgg != null) {
                            List<? extends Terms.Bucket> attrValueAggBuckets = attrValueAgg.getBuckets();
                            if (!CollectionUtils.isEmpty(attrValueAggBuckets)) {
                                //设置 attrValues
                                searchResponseAttrVo.setAttrValues(attrValueAggBuckets.stream()
                                        .map(Terms.Bucket::getKeyAsString).collect(Collectors.toList()));
                            }
                        }
                        return searchResponseAttrVo;
                    }).collect(Collectors.toList()));
                }
            }
        }
        return searchResponseVo;
    }

    private SearchSourceBuilder buildDsl(SearchParamVo searchParamVo) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //1、查询和过滤条件的构建
        String keyWord = searchParamVo.getKeyword();
        if (!StringUtils.isNotBlank(keyWord)) {
            //TODO：打广告
            return sourceBuilder;
        }
        //构建布尔查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        sourceBuilder.query(boolQueryBuilder);
        //  1.1、匹配查询
        boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyWord).operator(Operator.AND));
        //  1.2、过滤条件
        //      1.2.1、品牌过滤
        List<Long> brandId = searchParamVo.getBrandId();

        if (!CollectionUtils.isEmpty(brandId)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", brandId));
        }
        //      1.2.2、分类过滤
        List<Long> cid = searchParamVo.getCid();
        if (!CollectionUtils.isEmpty(cid)) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("categoryId", cid));
        }
        //      1.2.3、价格区间
        Double priceFrom = searchParamVo.getPriceFrom();
        Double priceTo = searchParamVo.getPriceTo();
        if (priceFrom != null || priceTo != null) {
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("price");
            if (priceFrom != null) {
                rangeQuery.gte(priceFrom);
            }
            if (priceTo != null) {
                rangeQuery.lte(priceTo);
            }
            boolQueryBuilder.filter(rangeQuery);
        }
        //      1.2.4、是否有货
        Boolean store = searchParamVo.getStore();
        if (store != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("store", store));
        }
        //      1.2.5、规格参数嵌套过滤  ===  {"4:8G-12G","5:256G-512G"}
        List<String> props = searchParamVo.getProps();
        if (!CollectionUtils.isEmpty(props)) {
            props.forEach(prop -> {
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                //解析字符串 4:8G-12G
                if (!StringUtils.isBlank(prop)) {
                    String[] attrs = StringUtils.split(prop, ":");
                    if (attrs != null && attrs.length == 2) {
                        boolQuery.must(QueryBuilders.termQuery("searchAttrs.attrId", attrs[0]));
                        //分割字符串  8G-12G
                        String[] attrValues = StringUtils.split(attrs[1], "-");
                        if (attrValues != null) {
                            boolQuery.must(QueryBuilders.termsQuery("searchAttrs.attrValue", attrValues));
                        }
                        boolQueryBuilder.filter(QueryBuilders.nestedQuery("searchAttrs", boolQuery, ScoreMode.None));
                    }
                }
            });
        }
        //2、排序条件过滤
        Integer sort = searchParamVo.getSort();
        if (sort != null) {
            //排序字段：0或者默认-得分降序  1-价格降序  2-价格升序  3-新品降序  4-销量降序
            switch (sort) {
                case 1:
                    sourceBuilder.sort("price", SortOrder.DESC);
                    break;
                case 2:
                    sourceBuilder.sort("price", SortOrder.ASC);
                    break;
                case 3:
                    sourceBuilder.sort("createTime", SortOrder.DESC);
                    break;
                case 4:
                    sourceBuilder.sort("sales", SortOrder.DESC);
                    break;
                default:
                    sourceBuilder.sort("_score", SortOrder.DESC);
                    break;
            }
        }
        //3、分页条件
        Integer pageNumber = searchParamVo.getPageNum();
        Integer pageSize = searchParamVo.getPageSize();
        //页码从1开始
        sourceBuilder.from((pageNumber - 1) * pageSize);
        sourceBuilder.size(pageSize);
        //4、高亮
        sourceBuilder.highlighter(new HighlightBuilder().field("title").preTags("<font style='color:red'>").postTags("</font>"));
        //5、聚合
        //  5.1 品牌的聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("brandIdAgg").field("brandId")
                .subAggregation(AggregationBuilders.terms("brandNameAgg").field("brandName"))
                .subAggregation(AggregationBuilders.terms("logoAgg").field("logo")));
        //  5.2 分类的聚合
        sourceBuilder.aggregation(AggregationBuilders.terms("categoryIdAgg").field("categoryId")
                .subAggregation(AggregationBuilders.terms("categoryNameAgg").field("categoryName")));
        //  5.3 规格参数的聚合
        sourceBuilder.aggregation(AggregationBuilders.nested("attrAgg", "searchAttrs")
                .subAggregation(AggregationBuilders.terms("attrIdAgg").field("searchAttrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAgg").field("searchAttrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAgg").field("searchAttrs.attrValue"))));

        //  6、构建结果集过滤
        sourceBuilder.fetchSource(new String[]{"skuId", "title", "subTitle", "price", "defaultImage"}, null);

        System.out.println(sourceBuilder);
        return sourceBuilder;
    }
}
